home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / domacnost a kancelar / joomla / Joomla_1.5.4-Stable-Full_Package.exe / libraries / pattemplate / patTemplate.php < prev   
PHP Script  |  2008-07-06  |  68KB  |  2,670 lines

  1. <?PHP
  2. /**
  3.  * patTemplate
  4.  *
  5.  * $Id: patTemplate.php 10381 2008-06-01 03:35:53Z pasamio $
  6.  *
  7.  * powerful templating engine
  8.  *
  9.  * @version        3.1.0
  10.  * @package        patTemplate
  11.  * @author        Stephan Schmidt <schst@php.net>
  12.  * @license        LGPL
  13.  * @link        http://www.php-tools.net
  14.  */
  15.  
  16. // ** Following line Joomla! specific **
  17. require_once( dirname( __FILE__ ) . '/patErrorManager.php' );
  18.  
  19. /**
  20.  * template already exists
  21.  */
  22. define( 'PATTEMPLATE_ERROR_TEMPLATE_EXISTS', 5010 );
  23.  
  24. /**
  25.  * template does not exist
  26.  */
  27. define ( 'PATTEMPLATE_WARNING_NO_TEMPLATE', 5011 );
  28.  
  29. /**
  30.  * unknown type
  31.  */
  32. define ( 'PATTEMPLATE_WARNING_UNKNOWN_TYPE', 5012 );
  33.  
  34. /**
  35.  * base class for module could not be found
  36.  */
  37. define( 'PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND', 5050 );
  38.  
  39. /**
  40.  * module could not be found
  41.  */
  42. define( 'PATTEMPLATE_ERROR_MODULE_NOT_FOUND', 5051 );
  43.  
  44. /**
  45.  * array expected
  46.  */
  47. define( 'PATTEMPLATE_ERROR_EXPECTED_ARRAY', 5052 );
  48.  
  49. /**
  50.  * No input
  51.  */
  52. define( 'PATTEMPLATE_ERROR_NO_INPUT', 6000 );
  53. /**
  54.  * Recursion
  55.  */
  56. define( 'PATTEMPLATE_ERROR_RECURSION', 6010 );
  57.  
  58. /**
  59.  * patTemplate
  60.  *
  61.  * powerful templating engine
  62.  *
  63.  * @version        3.1.0
  64.  * @package        patTemplate
  65.  * @author        Stephan Schmidt <schst@php.net>
  66.  * @license        LGPL
  67.  * @link        http://www.php-tools.net
  68.  */
  69. class patTemplate
  70. {
  71.     /**
  72.     * standard system vars that identify pat tools
  73.     * @var    array
  74.     */
  75.     var    $_systemVars            =    array(
  76.                                         'appName'        =>    'patTemplate',
  77.                                         'appVersion'    =>    '3.1.0',
  78.                                         'author'        =>    array(
  79.                                                                     'Stephan Schmidt <schst@php.net>'
  80.                                                                  )
  81.                                     );
  82.  
  83.     /**
  84.     * default attributes for new templates
  85.     * @access    private
  86.     * @var        array
  87.     */
  88.     var    $_defaultAttributes    =    array(
  89.                                         'type'            =>    'standard',
  90.                                         'visibility'    =>    'visible',
  91.                                         'loop'            =>    1,
  92.                                         'unusedvars'    =>    'strip',
  93.                                         'whitespace'    =>    'keep',
  94.                                         'autoclear'        =>    'off',
  95.                                         'autoload'        =>    'on'
  96.                                     );
  97.  
  98.     /**
  99.     * options for patTemplate
  100.     *
  101.     * Currently the following options are implemented:
  102.     * - maintainBc defines, whether patTemplate should be backwards compatible.
  103.     *   This means, that you may use 'default' and 'empty' for subtemplates.
  104.     *
  105.     * @access    private
  106.     * @var        array
  107.     */
  108.     var    $_options    =    array(
  109.                                 'startTag'            => '{',
  110.                                 'endTag'            => '}',
  111.                                 'root'                => array('__default' => '.'),
  112.                                 'namespace'            => 'patTemplate',
  113.                                 'maintainBc'        => true,
  114.                                 'defaultFunction'    => false
  115.                              );
  116.  
  117.     /**
  118.     * start tag
  119.     *
  120.     * @access    private
  121.     * @var        string
  122.     */
  123.     var $_startTag = '{';
  124.  
  125.     /**
  126.     * end tag
  127.     *
  128.     * @access    private
  129.     * @var        string
  130.     */
  131.     var $_endTag = '}';
  132.  
  133.     /**
  134.     * loaded modules
  135.     *
  136.     * Modules are:
  137.     * - Readers
  138.     * - Caches
  139.     * - Variable modifiers
  140.     * - Filters
  141.     *
  142.     * @access    private
  143.     * @var        array
  144.     */
  145.     var    $_modules        =    array();
  146.  
  147.     /**
  148.     * directories, where modules can be stored
  149.     * @access    private
  150.     * @var        array
  151.     */
  152.     var    $_moduleDirs    =    array();
  153.  
  154.     /**
  155.     * stores all template names
  156.     * @access    private
  157.     * @var        array
  158.     */
  159.     var    $_templateList    =    array();
  160.  
  161.     /**
  162.     * stores all template data
  163.     * @access    private
  164.     * @var        array
  165.     */
  166.     var    $_templates        =    array();
  167.  
  168.     /**
  169.     * stores all global variables
  170.     * @access    private
  171.     * @var        array
  172.     */
  173.     var    $_globals    =    array();
  174.  
  175.     /**
  176.     * stores all local variables
  177.     * @access    private
  178.     * @var        array
  179.     */
  180.     var    $_vars    =    array();
  181.  
  182.     /**
  183.     * stores the name of the first template that has been
  184.     * found
  185.     *
  186.     * @access    private
  187.     * @var        string
  188.     */
  189.     var    $_root;
  190.  
  191.     /**
  192.     * output filters that should be used
  193.     *
  194.     * @access    private
  195.     * @var        array
  196.     */
  197.     var    $_outputFilters = array();
  198.  
  199.     /**
  200.     * input filters that should be used
  201.     *
  202.     * @access    private
  203.     * @var        array
  204.     */
  205.     var    $_inputFilters = array();
  206.  
  207.     /**
  208.     * template cache, that should be used
  209.     *
  210.     * @access    private
  211.     * @var        array
  212.     */
  213.     var    $_tmplCache = null;
  214.  
  215.     /**
  216.     * placeholders, that have been discovered
  217.     *
  218.     * @access    private
  219.     * @var        array
  220.     */
  221.     var    $_discoveredPlaceholders = array();
  222.  
  223.     /**
  224.     * Create a new patTemplate instance.
  225.     *
  226.     * The constructor accepts the type of the templates as sole parameter.
  227.     * You may choose one of:
  228.     * - html (default)
  229.     * - tex
  230.     *
  231.     * The type influences the tags you are using in your templates.
  232.     *
  233.     * @access    public
  234.     * @param    string    type (either html or tex)
  235.     */
  236.     function patTemplate( $type = 'html' )
  237.     {
  238.         if( !defined( 'PATTEMPLATE_INCLUDE_PATH' ) ) {
  239.             define( 'PATTEMPLATE_INCLUDE_PATH', dirname( __FILE__ ) . '/patTemplate' );
  240.         }
  241.  
  242.         $this->setType( $type );
  243.     }
  244.  
  245.     /**
  246.     * sets an option
  247.     *
  248.     * Currently, the following options are supported
  249.     * - maintainBc (true|false)
  250.     * - namespace (string)
  251.     *
  252.     * @access    public
  253.     * @param    string    option to set
  254.     * @param    string    value of the option
  255.     */
  256.     function setOption($option, $value)
  257.     {
  258.         $this->_options[$option] = $value;
  259.     }
  260.  
  261.     /**
  262.     * gets an option
  263.     *
  264.     * @access    public
  265.     * @param    string    option to get
  266.     * @return    mixed    value of the option
  267.     */
  268.     function getOption( $option )
  269.     {
  270.         if (!isset($this->_options[$option])) {
  271.             return null;
  272.         }
  273.         return $this->_options[$option];
  274.     }
  275.  
  276.     /**
  277.     * sets name of directory where templates are stored
  278.     *
  279.     * @access    public
  280.     * @param    string    dir where templates are stored
  281.     * @deprecated        please use patTemplate::setRoot() instead
  282.     */
  283.     function setBasedir($basedir)
  284.     {
  285.         $this->setRoot($basedir);
  286.     }
  287.  
  288.     /**
  289.     * sets root base for the template
  290.     *
  291.     * The parameter depends on the reader you are using.
  292.     *
  293.     * @access    public
  294.     * @param    string    root base of the templates
  295.     */
  296.     function setRoot($root, $reader = '__default')
  297.     {
  298.         $this->_options['root'][$reader] = $root;
  299.     }
  300.  
  301.     /**
  302.     * gets name of root base for the templates
  303.     *
  304.     * @access    public
  305.     * @return    mixed         root base
  306.     */
  307.     function getRoot($reader = '__default')
  308.     {
  309.         return    $this->_options['root'][$reader];
  310.     }
  311.  
  312.     /**
  313.     * sets namespace of patTemplate tags
  314.     *
  315.     * If you want to use more than one namespace, you may set this to
  316.     * an array. All tags in these namespaces will be treated as patTemplate
  317.     * tags.
  318.     *
  319.     * @access    public
  320.     * @param    string|array    namespace(s)
  321.     */
  322.     function setNamespace($ns)
  323.     {
  324.         $this->_options['namespace'] = $ns;
  325.     }
  326.  
  327.     /**
  328.     * gets namespace of patTemplate tags
  329.     *
  330.     * @access    public
  331.     * @return    string|array    namespace(s)
  332.     */
  333.     function getNamespace()
  334.     {
  335.         return $this->_options['namespace'];
  336.     }
  337.  
  338.     /**
  339.     * set default attribute
  340.     *
  341.     * @access    public
  342.     * @param    string    attribute name
  343.     * @param    mixed    attribute value
  344.     */
  345.     function setDefaultAttribute( $name, $value )
  346.     {
  347.         $this->_defaultAttributes[$name]    =    $value;
  348.     }
  349.  
  350.     /**
  351.     * set default attributes
  352.     *
  353.     * @access    public
  354.     * @param    array    attributes
  355.     */
  356.     function setDefaultAttributes( $attributes )
  357.     {
  358.         $this->_defaultAttributes    =    array_merge( $this->_defaultAttributes, $attributes );
  359.     }
  360.  
  361.     /**
  362.     * get default attributes
  363.     *
  364.     * @access    public
  365.     * @return    return default attributes
  366.     */
  367.     function getDefaultAttributes()
  368.     {
  369.         return    $this->_defaultAttributes;
  370.     }
  371.  
  372.     /**
  373.     * set the type for the templates
  374.     *
  375.     * @access    public
  376.     * @param    string    type (html or tex)
  377.     * @return    boolean    true on success
  378.     */
  379.     function setType( $type )
  380.     {
  381.         switch( strtolower( $type ) )
  382.         {
  383.             case "tex":
  384.                 $this->setTags( '<{', '}>' );
  385.                 break;
  386.             case "html":
  387.                 $this->setTags( '{', '}' );
  388.                 break;
  389.             default:
  390.                 return    patErrorManager::raiseWarning(
  391.                                                         PATTEMPLATE_WARNING_UNKNOWN_TYPE,
  392.                                                         "Unknown type '$type'. Please use 'html' or 'tex'."
  393.                                                     );
  394.         }
  395.         return true;
  396.     }
  397.  
  398.     /**
  399.     * set the start and end tag for variables
  400.     *
  401.     * @access    public
  402.     * @param    string    start tag
  403.     * @param    string    end tag
  404.     * @return    boolean    true on success
  405.     */
  406.     function setTags( $startTag, $endTag )
  407.     {
  408.         $this->_options['startTag']    =    $startTag;
  409.         $this->_options['endTag']    =    $endTag;
  410.  
  411.         $this->_startTag    =    $startTag;
  412.         $this->_endTag        =    $endTag;
  413.         return true;
  414.     }
  415.  
  416.     /**
  417.     * get start tag for variables
  418.     *
  419.     * @access    public
  420.     * @return    string    start tag
  421.     */
  422.     function getStartTag()
  423.     {
  424.         return $this->_options['startTag'];
  425.     }
  426.  
  427.     /**
  428.     * get end tag for variables
  429.     *
  430.     * @access    public
  431.     * @return    string    end tag
  432.     */
  433.     function getEndTag()
  434.     {
  435.         return $this->_options['endTag'];
  436.     }
  437.  
  438.     /**
  439.     * add a directory where patTemplate should search for
  440.     * modules.
  441.     *
  442.     * You may either pass a string or an array of directories.
  443.     *
  444.     * patTemplate will be searching for a module in the same
  445.     * order you added them. If the module cannot be found in
  446.     * the custom folders, it will look in
  447.     * patTemplate/$moduleType.
  448.     *
  449.     * @access    public
  450.     * @param    string            module type
  451.     * @param    string|array    directory or directories to search.
  452.     */
  453.     function addModuleDir( $moduleType, $dir )
  454.     {
  455.         if( !isset( $this->_moduleDirs[$moduleType] ) )
  456.             $this->_moduleDirs[$moduleType]    =    array();
  457.         if( is_array( $dir ) )
  458.             $this->_moduleDirs[$moduleType] = array_merge( $this->_moduleDirs[$moduleType], $dir );
  459.         else
  460.             array_push( $this->_moduleDirs[$moduleType], $dir );
  461.     }
  462.  
  463.     /**
  464.     * Sets an attribute of a template
  465.     *
  466.     * supported attributes: visibilty, loop, parse, unusedvars
  467.     *
  468.     * @param    string    $template    name of the template
  469.     * @param    string    $attribute    name of the attribute
  470.     * @param    mixed    $value    value of the attribute
  471.     * @access    public
  472.     * @see        setAttributes(),getAttribute(), clearAttribute()
  473.     */
  474.     function setAttribute( $template, $attribute, $value )
  475.     {
  476.         $template    =    strtolower( $template );
  477.         if( !isset( $this->_templates[$template] ) )
  478.         {
  479.             return    patErrorManager::raiseWarning(
  480.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  481.                                                     "Template '$template' does not exist."
  482.                                                 );
  483.         }
  484.  
  485.         $attribute    =    strtolower( $attribute );
  486.         $this->_templates[$template]['attributes'][$attribute]    =    $value;
  487.         return true;
  488.     }
  489.  
  490.     /**
  491.     * Sets several attribute of a template
  492.     *
  493.     * $attributes has to be a assotiative arrays containing attribute/value pairs
  494.     * supported attributes: visibilty, loop, parse, unusedvars
  495.     *
  496.     * @param    string    $template    name of the template
  497.     * @param    array    $attributes    attribute/value pairs
  498.     * @access    public
  499.     * @see        setAttribute(), getAttribute(), clearAttribute()
  500.     */
  501.     function setAttributes( $template, $attributes )
  502.     {
  503.         if( !is_array( $attributes ) )
  504.         {
  505.             return patErrorManager::raiseError( PATTEMPLATE_ERROR_EXPECTED_ARRAY, 'patTemplate::setAttributes: Expected array as second parameter, '.gettype( $attributes ).' given' );
  506.         }
  507.  
  508.         $template    =    strtolower( $template );
  509.         $attributes    =    array_change_key_case( $attributes );
  510.         if( !isset( $this->_templates[$template] ) )
  511.         {
  512.             return    patErrorManager::raiseWarning(
  513.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  514.                                                     "Template '$template' does not exist."
  515.                                                 );
  516.         }
  517.  
  518.         $this->_templates[$template]['attributes']    =    array_merge( $this->_templates[$template]['attributes'], $attributes );
  519.         return true;
  520.     }
  521.  
  522.     /**
  523.     * Get all attributes of a template
  524.     *
  525.     * @param    string    name of the template
  526.     * @return    array    attributes
  527.     * @access    public
  528.     */
  529.     function getAttributes( $template )
  530.     {
  531.         $template    =    strtolower( $template );
  532.         if( !isset( $this->_templates[$template] ) )
  533.         {
  534.             return    patErrorManager::raiseWarning(
  535.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  536.                                                     "Template '$template' does not exist."
  537.                                                 );
  538.         }
  539.         return    $this->_templates[$template]['attributes'];
  540.     }
  541.  
  542.     /**
  543.     * Gets an attribute of a template
  544.     *
  545.     * supported attributes: visibilty, loop, parse, unusedvars
  546.     *
  547.     * @param    string    $template    name of the template
  548.     * @param    string    $attribute    name of the attribute
  549.     * @return    mixed    value of the attribute
  550.     * @access    public
  551.     * @see        setAttribute(), setAttributes(), clearAttribute()
  552.     */
  553.     function getAttribute( $template, $attribute )
  554.     {
  555.         $template    =    strtolower( $template );
  556.         $attribute    =    strtolower( $attribute );
  557.         if( !isset( $this->_templates[$template] ) )
  558.         {
  559.             return    patErrorManager::raiseWarning(
  560.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  561.                                                     "Template '$template' does not exist."
  562.                                                 );
  563.         }
  564.         return    $this->_templates[$template]['attributes'][$attribute];
  565.     }
  566.  
  567.     /**
  568.     * Clears an attribute of a template
  569.     *
  570.     * supported attributes: visibilty, loop, parse, unusedvars
  571.     *
  572.     * @param    string    $template    name of the template
  573.     * @param    string    $attribute    name of the attribute
  574.     * @access    public
  575.     * @see        setAttribute(), setAttributes(), getAttribute()
  576.     */
  577.     function clearAttribute( $template, $attribute )
  578.     {
  579.         $template    =    strtolower( $template );
  580.         $attribute    =    strtolower( $attribute );
  581.  
  582.         if( !isset( $this->_templates[$template] ) )
  583.         {
  584.             return    patErrorManager::raiseWarning(
  585.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  586.                                                     "Template '$template' does not exist."
  587.                                                 );
  588.         }
  589.         $this->_templates[$template]['attributes'][$attribute]    =    '';;
  590.         return true;
  591.     }
  592.  
  593.     /**
  594.     * Prepare a template
  595.     *
  596.     * This can be used if you want to add variables to
  597.     * a template, that has not been loaded yet.
  598.     *
  599.     * @access    public
  600.     * @param    string    template name
  601.     */
  602.     function prepareTemplate( $name )
  603.     {
  604.         $name    =    strtolower( $name );
  605.         if( !isset( $this->_vars[$name] ) )
  606.         {
  607.             $this->_vars[$name]    =    array(
  608.                                                 'scalar'    =>    array(),
  609.                                                 'rows'        =>    array()
  610.                                             );
  611.         }
  612.     }
  613.  
  614.     /**
  615.     * add a variable to a template
  616.     *
  617.     * A variable may also be an indexed array, but _not_
  618.     * an associative array!
  619.     *
  620.     * @access    public
  621.     * @param    string    $template    name of the template
  622.     * @param    string    $varname    name of the variable
  623.     * @param    mixed    $value        value of the variable
  624.     */
  625.     function addVar( $template, $varname, $value )
  626.     {
  627.         $template = strtolower( $template );
  628.         $varname  = strtoupper( $varname );
  629.  
  630.         if( !is_array( $value ) ) {
  631.             $this->_vars[$template]['scalar'][$varname] = $value;
  632.             return true;
  633.         }
  634.  
  635.         $cnt = count( $value );
  636.         for ($i = 0; $i < $cnt; $i++) {
  637.             if (!isset( $this->_vars[$template]['rows'][$i] )) {
  638.                 $this->_vars[$template]['rows'][$i] = array();
  639.             }
  640.             $this->_vars[$template]['rows'][$i][$varname] = $value[$i];
  641.         }
  642.  
  643.         return true;
  644.     }
  645.  
  646.     /**
  647.     * get the value of a variable
  648.     *
  649.     * @access    public
  650.     * @param    string    name of the template
  651.     * @param    string    name of the variable
  652.     * @return    string    value of the variable, null if the variable is not set
  653.     */
  654.     function getVar( $template, $varname )
  655.     {
  656.         $template    =    strtolower( $template );
  657.         $varname    =    strtoupper( $varname );
  658.  
  659.         if( isset( $this->_vars[$template]['scalar'][$varname] ) )
  660.             return $this->_vars[$template]['scalar'][$varname];
  661.  
  662.         $value = array();
  663.  
  664.         if(!isset($this->_vars[$template]['rows']))
  665.             return null;
  666.  
  667.         $cnt = count( $this->_vars[$template]['rows'] );
  668.         for( $i = 0; $i < $cnt; $i++ )
  669.         {
  670.             if( !isset( $this->_vars[$template]['rows'][$i][$varname] ) )
  671.                 continue;
  672.             array_push( $value, $this->_vars[$template]['rows'][$i][$varname] );
  673.         }
  674.         if( !empty( $value ) )
  675.             return $value;
  676.         return null;
  677.     }
  678.  
  679.     /**
  680.     * clear the value of a variable
  681.     *
  682.     * @access    public
  683.     * @param    string    name of the template
  684.     * @param    string    name of the variable
  685.     * @return   boolean
  686.     * @see      clearVars(), clearTemplate()
  687.     */
  688.     function clearVar( $template, $varname )
  689.     {
  690.         $template    =    strtolower( $template );
  691.         $varname    =    strtoupper( $varname );
  692.  
  693.         if (isset( $this->_vars[$template]['scalar'][$varname] )) {
  694.             unset ($this->_vars[$template]['scalar'][$varname]);
  695.             return true;
  696.         }
  697.  
  698.         $result = false;
  699.         $cnt = count( $this->_vars[$template]['rows'] );
  700.         for ($i = 0; $i < $cnt; $i++) {
  701.             if (!isset($this->_vars[$template]['rows'][$i][$varname])) {
  702.                 continue;
  703.             }
  704.             unset($this->_vars[$template]['rows'][$i][$varname]);
  705.             $result = true;
  706.         }
  707.         return $result;
  708.     }
  709.  
  710.  
  711.     /**
  712.     * Adds several variables to a template
  713.     *
  714.     * Each Template can have an unlimited amount of its own variables
  715.     * $variables has to be an assotiative array containing variable/value pairs
  716.     *
  717.     * @param    string    $template    name of the template
  718.     * @param    array    $variables    assotiative array of the variables
  719.     * @param    string    $prefix    prefix for all variable names
  720.     * @access    public
  721.     * @see        addVar(), addRows(), addGlobalVar(), addGlobalVars()
  722.     */
  723.     function addVars( $template, $variables, $prefix = '' )
  724.     {
  725.         $template    =    strtolower( $template );
  726.         $prefix        =    strtoupper( $prefix );
  727.         $variables    =    array_change_key_case( $variables, CASE_UPPER );
  728.  
  729.         foreach ($variables as $varname => $value) {
  730.             $varname = $prefix.$varname;
  731.  
  732.             if (!is_array($value)) {
  733.                 if (!is_scalar($value)) {
  734.                     continue;
  735.                 }
  736.                 $this->_vars[$template]['scalar'][$varname] = $value;
  737.                 continue;
  738.             }
  739.  
  740.             $cnt = count( $value );
  741.             for( $i = 0; $i < $cnt; $i++ ) {
  742.                 if( !isset( $this->_vars[$template]['rows'][$i] ) )
  743.                     $this->_vars[$template]['rows'][$i]    =    array();
  744.  
  745.                 $this->_vars[$template]['rows'][$i][$varname]    =    $value[$i];
  746.             }
  747.         }
  748.     }
  749.  
  750.     /**
  751.     * Clear all variables in a template
  752.     *
  753.     * This clears only variables, but does
  754.     *
  755.     * @access    public
  756.     * @param    string    $template    name of the template
  757.     * @return   boolean
  758.     * @see        clearVar(), clearTemplate()
  759.     */
  760.     function clearVars( $template )
  761.     {
  762.         $template = strtolower($template);
  763.         $this->_vars[$template] = array(
  764.                                          'scalar' => array(),
  765.                                          'rows'   => array()
  766.                                         );
  767.         return true;
  768.     }
  769.  
  770.  
  771.     /**
  772.     * Adds several rows of variables to a template
  773.     *
  774.     * Each Template can have an unlimited amount of its own variables
  775.     * Can be used to add a database result as variables to a template
  776.     *
  777.     * @param    string    $template    name of the template
  778.     * @param    array    $rows    array containing assotiative arrays with variable/value pairs
  779.     * @param    string    $prefix    prefix for all variable names
  780.     * @access    public
  781.     * @see        addVar(), addVars(), addGlobalVar(), addGlobalVars()
  782.     */
  783.     function addRows( $template, $rows, $prefix = '' )
  784.     {
  785.         $template    =    strtolower( $template );
  786.         $prefix        =    strtoupper( $prefix );
  787.  
  788.         $cnt        =    count( $rows );
  789.         for( $i = 0; $i < $cnt; $i++ )
  790.         {
  791.             if( !isset( $this->_vars[$template]['rows'][$i] ) )
  792.                 $this->_vars[$template]['rows'][$i]    =    array();
  793.  
  794.             $rows[$i]    =    array_change_key_case( $rows[$i], CASE_UPPER );
  795.  
  796.             foreach( $rows[$i] as $varname => $value )
  797.             {
  798.                 $this->_vars[$template]['rows'][$i][$prefix.$varname]    =    $value;
  799.             }
  800.         }
  801.     }
  802.  
  803.     /**
  804.     * Adds an object to a template
  805.     *
  806.     * All properties of the object will be available as template variables.
  807.     *
  808.     * @access    public
  809.     * @param    string            name of the template
  810.     * @param    object|array    object or array of objects
  811.     * @param    string            prefix for all variable names
  812.     * @param    boolean            ignore private properties (starting with _)
  813.     * @see        addVar(), addRows(), addGlobalVar(), addGlobalVars()
  814.     */
  815.     function addObject( $template, $object, $prefix = '', $ignorePrivate = false )
  816.     {
  817.         if( is_array( $object ) ) {
  818.             $rows = array();
  819.             foreach($object as $o) {
  820.                 array_push( $rows, $this->getObjectVars($o, $ignorePrivate) );
  821.             }
  822.  
  823.                return $this->addRows( $template, $rows, $prefix );
  824.         } elseif (is_object($object)) {
  825.             return $this->addVars( $template, $this->getObjectVars($object, $ignorePrivate), $prefix );
  826.         }
  827.         return false;
  828.     }
  829.  
  830.     /**
  831.     * get the vars from an object
  832.     *
  833.     * @access   private
  834.     * @param    object
  835.     * @param    boolean     ignore private properties (starting with _)
  836.     * @return   array
  837.     */
  838.     function getObjectVars($obj, $ignorePrivate = false)
  839.     {
  840.         if (method_exists($obj, 'getVars')) {
  841.             return $obj->getVars();
  842.         }
  843.         $vars = get_object_vars($obj);
  844.         if ($ignorePrivate === false) {
  845.             return $vars;
  846.         }
  847.         foreach ($vars as $var => $value) {
  848.             if ($var{0} == '_') {
  849.                 unset($vars[$var]);
  850.             }
  851.         }
  852.         return $vars;
  853.     }
  854.  
  855.     /**
  856.     * Adds a global variable
  857.     *
  858.     * Global variables are valid in all templates of this object.
  859.     * A global variable has to be scalar, it will be converted to a string.
  860.     *
  861.     * @access    public
  862.     * @param    string    $varname    name of the global variable
  863.     * @param    string    $value        value of the variable
  864.     * @return    boolean    true on success
  865.     * @see        addGlobalVars(), addVar(), addVars(), addRows()
  866.     */
  867.     function addGlobalVar( $varname, $value )
  868.     {
  869.         $this->_globals[strtoupper( $varname )]    =    ( string )$value;
  870.         return    true;
  871.     }
  872.  
  873.     /**
  874.     * Clears a global variable
  875.     *
  876.     * @access    public
  877.     * @param    string    $varname    name of the global variable
  878.     * @return    boolean    true on success
  879.     * @see        clearVar(), clearVars(), clearGlobalVars()
  880.     */
  881.     function clearGlobalVar( $varname )
  882.     {
  883.         $varname = strtoupper( $varname );
  884.         if (!isset($this->_globals[$varname])) {
  885.             return false;
  886.         }
  887.         unset($this->_globals[$varname]);
  888.         return    true;
  889.     }
  890.  
  891.     /**
  892.     * Clears all global variables
  893.     *
  894.     * @access    public
  895.     * @return    boolean    true on success
  896.     * @see        clearVar(), clearVars(), clearGlobalVar()
  897.     */
  898.     function clearGlobalVars()
  899.     {
  900.         $this->_globals = array();
  901.         return    true;
  902.     }
  903.  
  904.     /**
  905.     * Adds several global variables
  906.     *
  907.     * Global variables are valid in all templates of this object.
  908.     *
  909.     * $variables is an associative array, containing name/value pairs of the variables.
  910.     *
  911.     * @access    public
  912.     * @param    array    $variables    array containing the variables
  913.     * @param    string    $prefix        prefix for variable names
  914.     * @return    boolean    true on success
  915.     * @see        addGlobalVar(), addVar(), addVars(), addRows()
  916.     */
  917.     function addGlobalVars( $variables, $prefix = '' )
  918.     {
  919.         $variables    =    array_change_key_case( $variables, CASE_UPPER );
  920.         $prefix        =    strtoupper( $prefix );
  921.         foreach( $variables as $varname => $value )
  922.         {
  923.             $this->_globals[$prefix.$varname]    =    ( string )$value;
  924.         }
  925.  
  926.         return    true;
  927.     }
  928.  
  929.     /**
  930.     * get all global variables
  931.     *
  932.     * @access    public
  933.     * @return    array    global variables
  934.     */
  935.     function getGlobalVars()
  936.     {
  937.         return    $this->_globals;
  938.     }
  939.  
  940.     /**
  941.     * checks wether a template exists
  942.     *
  943.     * @access    public
  944.     * @param    string        name of the template
  945.     * @return    boolean        true, if the template exists, false otherwise
  946.     */
  947.     function exists( $name )
  948.     {
  949.         return    in_array( strtolower( $name ), $this->_templateList );
  950.     }
  951.  
  952.     /**
  953.     * enable a template cache
  954.     *
  955.     * A template cache will improve performace, as the templates
  956.     * do not have to be read on each request.
  957.     *
  958.     * @access    public
  959.     * @param    string        name of the template cache
  960.     * @param    array        parameters for the template cache
  961.     * @return    boolean        true on success, patError otherwise
  962.     */
  963.     function useTemplateCache( $cache, $params = array() )
  964.     {
  965.         if( !is_object( $cache ) )
  966.         {
  967.             $cache = &$this->loadModule( 'TemplateCache', $cache, $params );
  968.         }
  969.         if( patErrorManager::isError( $cache ) )
  970.             return $cache;
  971.  
  972.         $this->_tmplCache = &$cache;
  973.         return true;
  974.     }
  975.  
  976.     /**
  977.     * enable an output filter
  978.     *
  979.     * Output filters are used to modify the template
  980.     * result before it is sent to the browser.
  981.     *
  982.     * They are applied, when displayParsedTemplate() is called.
  983.     *
  984.     * @access    public
  985.     * @param    string        name of the output filter
  986.     * @param    array        parameters for the output filter
  987.     * @return    boolean        true on success, patError otherwise
  988.     */
  989.     function applyOutputFilter( $filter, $params = array(), $template = null )
  990.     {
  991.         if (!is_object($filter)) {
  992.             $filter = &$this->loadModule( 'OutputFilter', $filter, $params );
  993.         }
  994.         if (patErrorManager::isError($filter)) {
  995.             return $filter;
  996.         }
  997.  
  998.         if ($template === null) {
  999.             $this->_outputFilters[] = &$filter;
  1000.             return true;
  1001.         }
  1002.  
  1003.         $template = strtolower($template);
  1004.         if (!$this->exists($template)) {
  1005.             return patErrorManager::raiseWarning(PATTEMPLATE_WARNING_NO_TEMPLATE, 'The selected template does not exist');
  1006.         }
  1007.         $this->_templates[$template]['attributes']['outputfilter'] = &$filter;
  1008.         return true;
  1009.     }
  1010.  
  1011.     /**
  1012.     * enable an input filter
  1013.     *
  1014.     * input filters are used to modify the template
  1015.     * stream before it is split into smaller templates-
  1016.     *
  1017.     * @access    public
  1018.     * @param    string        name of the input filter
  1019.     * @param    array        parameters for the input filter
  1020.     * @return    boolean        true on success, patError otherwise
  1021.     */
  1022.     function applyInputFilter( $filter, $params = array() )
  1023.     {
  1024.         if( !is_object( $filter ) )
  1025.         {
  1026.             $filter = &$this->loadModule( 'InputFilter', $filter, $params );
  1027.         }
  1028.         if( patErrorManager::isError( $filter ) )
  1029.             return $filter;
  1030.  
  1031.         $this->_inputFilters[] = &$filter;
  1032.         return true;
  1033.     }
  1034.  
  1035.     /**
  1036.     * open a file and parse for patTemplate tags
  1037.     *
  1038.     * @access        public
  1039.     * @param        name of the file
  1040.     * @return        true, if the template could be parsed
  1041.     * @deprecated    Use patTemplate::readTemplatesFromInput() instead, as the method name is misleading
  1042.     * @see            readTemplatesFromInput()
  1043.     */
  1044.     function readTemplatesFromFile( $filename )
  1045.     {
  1046.         return    $this->readTemplatesFromInput( $filename, 'File' );
  1047.     }
  1048.  
  1049.     /**
  1050.     * open any input and parse for patTemplate tags
  1051.     *
  1052.     * @access    public
  1053.     * @param    string    name of the input (filename, shm segment, etc.)
  1054.     * @param    string    driver that is used as reader, you may also pass a Reader object
  1055.     * @param    array    additional options that will only be used for this template
  1056.     * @param    string    name of the template that should be used as a container, should not be used by public
  1057.     *                    calls.
  1058.     * @return    boolean    true, if the template could be parsed, false otherwise
  1059.     */
  1060.     function readTemplatesFromInput( $input, $reader = 'File', $options = null, $parseInto = null )
  1061.     {
  1062.         if ((string)$input === '') {
  1063.             return patErrorManager::raiseError(PATTEMPLATE_ERROR_NO_INPUT, 'No input to read has been passed.');
  1064.         }
  1065.  
  1066.         if (is_array($options)) {
  1067.             $options = array_merge( $this->_options, $options );
  1068.         } else {
  1069.             $options = $this->_options;
  1070.         }
  1071.  
  1072.         if (!is_null($parseInto)) {
  1073.             $parseInto    =    strtolower( $parseInto );
  1074.         }
  1075.  
  1076.         $templates = false;
  1077.         if ($this->_tmplCache !== null) {
  1078.             /**
  1079.              * get the unique cache key
  1080.              */
  1081.             $key = $this->_tmplCache->getKey($input, $options);
  1082.  
  1083.             $templates = $this->_loadTemplatesFromCache( $input, $reader, $options, $key );
  1084.  
  1085.             /**
  1086.              * check for error returned from cache
  1087.              */
  1088.             if (patErrorManager::isError($templates)) {
  1089.                 return $templates;
  1090.             }
  1091.         }
  1092.  
  1093.         /**
  1094.          * templates have not been loaded from cache
  1095.          */
  1096.         if ($templates === false) {
  1097.             if (!is_object( $reader)) {
  1098.                 $reader = &$this->loadModule('Reader', $reader);
  1099.                 if (patErrorManager::isError($reader)) {
  1100.                     return $reader;
  1101.                 }
  1102.             }
  1103.  
  1104.             if ($reader->isInUse()) {
  1105.                 $reader = &$this->loadModule( 'Reader', $reader->getName(), array(), true);
  1106.                 if( patErrorManager::isError( $reader ) ) {
  1107.                     return $reader;
  1108.                 }
  1109.             }
  1110.  
  1111.             $reader->setOptions($options);
  1112.  
  1113.             /**
  1114.              * set the root attributes
  1115.              */
  1116.             if( !is_null( $parseInto ) )
  1117.             {
  1118.                 $attributes = $this->getAttributes( $parseInto );
  1119.                 if( !patErrorManager::isError( $attributes ) )
  1120.                 {
  1121.                     $reader->setRootAttributes( $attributes );
  1122.                 }
  1123.             }
  1124.  
  1125.             $templates    =    $reader->readTemplates( $input );
  1126.  
  1127.             /**
  1128.              * check for error returned from reader
  1129.              */
  1130.             if( patErrorManager::isError( $templates ) )
  1131.                 return $templates;
  1132.  
  1133.             /**
  1134.              * store the
  1135.              */
  1136.             if( $this->_tmplCache !== null )
  1137.             {
  1138.                 $this->_tmplCache->write( $key, $templates );
  1139.             }
  1140.         }
  1141.  
  1142.         /**
  1143.          * traverse all templates
  1144.          */
  1145.         foreach( $templates as $name => $spec )
  1146.         {
  1147.  
  1148.             /**
  1149.              * root template
  1150.              */
  1151.             if( $name == '__ptroot' )
  1152.             {
  1153.                 if( $parseInto === false )
  1154.                 {
  1155.                     continue;
  1156.                 }
  1157.                 if( !in_array( $parseInto, $this->_templateList ) )
  1158.                     continue;
  1159.  
  1160.                 $spec['loaded']        = true;
  1161.                 $spec['attributes']    = $this->_templates[$parseInto]['attributes'];
  1162.                 $name    =    $parseInto;
  1163.             }
  1164.             else
  1165.             {
  1166.                 /**
  1167.                  * store the name
  1168.                  */
  1169.                 array_push( $this->_templateList, $name );
  1170.             }
  1171.  
  1172.             /**
  1173.              * if this is the first template that has been loaded
  1174.              * set it as the root template
  1175.              */
  1176.             if( $this->_root === null && is_null( $parseInto ) && isset( $spec['isRoot'] ) && $spec['isRoot'] == true )
  1177.             {
  1178.                 $this->_root = $name;
  1179.             }
  1180.  
  1181.             /**
  1182.              * set some default values
  1183.              */
  1184.             $spec['iteration']            =    0;
  1185.             $spec['lastMode']            =    'w';
  1186.             $spec['result']                =    '';
  1187.             $spec['modifyVars']            =    array();
  1188.             $spec['copyVars']            =    array();
  1189.             $spec['defaultVars']        =    array();
  1190.  
  1191.             /**
  1192.              * store the template
  1193.              */
  1194.             $this->_templates[$name]    =    $spec;
  1195.  
  1196.             $this->prepareTemplate( $name );
  1197.  
  1198.             /**
  1199.              * store the default values of the variables
  1200.              */
  1201.             foreach( $spec['varspecs'] as $varname => $varspec )
  1202.             {
  1203.                 if (isset($varspec['modifier'])) {
  1204.                     $this->_templates[$name]['modifyVars'][$varname] = $varspec['modifier'];
  1205.                 }
  1206.  
  1207.                 if( isset( $varspec['copyfrom'] ) )
  1208.                 {
  1209.                     $this->_templates[$name]['copyVars'][$varname] = $varspec['copyfrom'];
  1210.                 }
  1211.  
  1212.                 if( !isset( $varspec['default'] ) )
  1213.                     continue;
  1214.  
  1215.                 $this->_templates[$name]['defaultVars'][$varname] = $varspec['default'];
  1216.  
  1217.                 if( !is_null( $this->getVar( $name, $varname ) ) )
  1218.                     continue;
  1219.  
  1220.                 $this->addVar( $name, $varname, $varspec['default'] );
  1221.             }
  1222.  
  1223.             unset($this->_templates[$name]['varspecs']);
  1224.  
  1225.             /**
  1226.              * autoload the template
  1227.              *
  1228.              * Some error management is needed here...
  1229.              */
  1230.             if( isset( $this->_templates[$name]['attributes']['src'] ) && $this->_templates[$name]['attributes']['autoload'] == 'on' )
  1231.             {
  1232.                 if( $this->_templates[$name]['loaded'] !== true )
  1233.                 {
  1234.                     if( $this->_templates[$name]['attributes']['parse'] == 'on' )
  1235.                     {
  1236.                         $this->readTemplatesFromInput( $this->_templates[$name]['attributes']['src'], $this->_templates[$name]['attributes']['reader'], $options, $name );
  1237.                     }
  1238.                     else
  1239.                     {
  1240.                         $this->loadTemplateFromInput( $this->_templates[$name]['attributes']['src'], $this->_templates[$name]['attributes']['reader'], null, $name );
  1241.                     }
  1242.                     $this->_templates[$name]['loaded']    =    true;
  1243.                 }
  1244.             }
  1245.         }
  1246.  
  1247.         return true;
  1248.     }
  1249.  
  1250.     /**
  1251.     * load from template cache
  1252.     *
  1253.     * @access    private
  1254.     * @param    string    name of the input (filename, shm segment, etc.)
  1255.     * @param    string    driver that is used as reader, you may also pass a Reader object
  1256.     * @param    array    options for the reader
  1257.     * @param    string    cache key
  1258.     * @return    array|boolean    either an array containing the templates, or false
  1259.     */
  1260.     function _loadTemplatesFromCache( $input, &$reader, $options, $key )
  1261.     {
  1262.         if( is_object( $reader ) )
  1263.             $statName   =   $reader->getName();
  1264.         else
  1265.             $statName    =    $reader;
  1266.  
  1267.         $stat    =    &$this->loadModule( 'Stat', $statName );
  1268.         $stat->setOptions( $options );
  1269.  
  1270.         /**
  1271.          * get modification time
  1272.          */
  1273.         $modTime   = $stat->getModificationTime( $input );
  1274.         $templates = $this->_tmplCache->load( $key, $modTime );
  1275.  
  1276.         return $templates;
  1277.     }
  1278.  
  1279.     /**
  1280.     * open any input and load content into template
  1281.     *
  1282.     * @access    public
  1283.     * @param    string    name of the input (filename, shm segment, etc.)
  1284.     * @param    string    driver that is used as reader
  1285.     * @param    string    name of the template that should be used as a container,
  1286.     * @return    boolean    true, if the template could be parsed, false otherwise
  1287.     */
  1288.     function loadTemplateFromInput( $input, $reader = 'File', $options = null, $parseInto = false )
  1289.     {
  1290.         if( is_array( $options ) )
  1291.             $options = array_merge( $this->_options, $options );
  1292.         else
  1293.             $options = $this->_options;
  1294.  
  1295.         if( !is_null( $parseInto ) )
  1296.             $parseInto    =    strtolower( $parseInto );
  1297.  
  1298.         $reader    = &$this->loadModule( 'Reader', $reader );
  1299.         if( patErrorManager::isError( $reader ) )
  1300.         {
  1301.             return $reader;
  1302.         }
  1303.         $reader->setOptions($options);
  1304.  
  1305.         $result    = $reader->loadTemplate( $input );
  1306.  
  1307.         if( patErrorManager::isError( $result ) )
  1308.         {
  1309.             return $result;
  1310.         }
  1311.  
  1312.         $this->_templates[$parseInto]['content'] .= $result;
  1313.         $this->_templates[$parseInto]['loaded']   = true;
  1314.         return true;
  1315.     }
  1316.  
  1317.     /**
  1318.     * load a template that had autoload="off"
  1319.     *
  1320.     * This is needed, if you change the source of a template and want to
  1321.     * load it, after changing the attribute.
  1322.     *
  1323.     * @access    public
  1324.     * @param    string        template name
  1325.     * @return    boolean        true, if template could be loaded
  1326.     */
  1327.     function  loadTemplate( $template )
  1328.     {
  1329.         $template = strtolower( $template );
  1330.         if( !isset( $this->_templates[$template] ) )
  1331.         {
  1332.             return    patErrorManager::raiseWarning(
  1333.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  1334.                                                     "Template '$template' does not exist."
  1335.                                                 );
  1336.         }
  1337.  
  1338.         if( $this->_templates[$template]['loaded'] === true )
  1339.             return true;
  1340.  
  1341.         if( $this->_templates[$template]['attributes']['parse'] == 'on' )
  1342.         {
  1343.             return $this->readTemplatesFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
  1344.         }
  1345.         else
  1346.         {
  1347.             return $this->loadTemplateFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
  1348.         }
  1349.     }
  1350.  
  1351.     /**
  1352.     * loads a patTemplate module
  1353.     *
  1354.     * Modules are located in the patTemplate folder and include:
  1355.     * - Readers
  1356.     * - Caches
  1357.     * - Variable Modifiers
  1358.     * - Filters
  1359.     * - Functions
  1360.     * - Stats
  1361.     *
  1362.     * @access    public
  1363.     * @param    string    moduleType (Reader|TemplateCache|Modifier|OutputFilter|InputFilter)
  1364.     * @param    string    moduleName
  1365.     * @param    array    parameters for the module
  1366.     * @return    object
  1367.     */
  1368.     function &loadModule( $moduleType, $moduleName, $params = array(), $new = false )
  1369.     {
  1370.         if( !isset( $this->_modules[$moduleType] ) )
  1371.             $this->_modules[$moduleType]    =    array();
  1372.  
  1373.         $sig = md5( $moduleName . serialize( $params ) );
  1374.  
  1375.         if( isset( $this->_modules[$moduleType][$sig] ) && $new === false ) {
  1376.             return    $this->_modules[$moduleType][$sig];
  1377.         }
  1378.  
  1379.         if( !class_exists( 'patTemplate_Module' ) )
  1380.         {
  1381.             $file    =    sprintf( "%s/Module.php", $this->getIncludePath() );
  1382.             if( !file_exists( $file ) or !include_once $file )
  1383.                 return    patErrorManager::raiseError( PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND, 'Could not load module base class.' );
  1384.         }
  1385.  
  1386.         $baseClass    =    'patTemplate_' . $moduleType;
  1387.         if( !class_exists( $baseClass ) )
  1388.         {
  1389.             $baseFile    =    sprintf( "%s/%s.php", $this->getIncludePath(), $moduleType );
  1390.             if( !file_exists( $baseFile ) or !include_once $baseFile )
  1391.                 return    patErrorManager::raiseError( PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND, "Could not load base class for $moduleType ($baseFile)." );
  1392.         }
  1393.  
  1394.         $moduleClass    =    'patTemplate_' . $moduleType . '_' .$moduleName;
  1395.         if( !class_exists( $moduleClass ) )
  1396.         {
  1397.             if( isset( $this->_moduleDirs[$moduleType] ) )
  1398.                 $dirs = $this->_moduleDirs[$moduleType];
  1399.             else
  1400.                 $dirs = array();
  1401.             array_push( $dirs, $this->getIncludePath() .'/'. $moduleType );
  1402.  
  1403.             $found = false;
  1404.             foreach( $dirs as $dir )
  1405.             {
  1406.                 $moduleFile    = sprintf( "%s/%s.php", $dir, str_replace( '_', '/', $moduleName ) );
  1407.  
  1408.                 if ( file_exists( $moduleFile ) and include_once $moduleFile) {
  1409.                     $found = true;
  1410.                     break;
  1411.                 }
  1412.             }
  1413.  
  1414.             if( !$found ) {
  1415.                 return    patErrorManager::raiseError( PATTEMPLATE_ERROR_MODULE_NOT_FOUND, "Could not load module $moduleClass ($moduleFile)." );
  1416.             }
  1417.         }
  1418.  
  1419.         if( !class_exists( $moduleClass ) )
  1420.         {
  1421.             return    patErrorManager::raiseError( PATTEMPLATE_ERROR_MODULE_NOT_FOUND, "Module file $moduleFile does not contain class $moduleClass." );
  1422.         }
  1423.  
  1424.         $this->_modules[$moduleType][$sig]    =    &new $moduleClass;
  1425.         if( method_exists( $this->_modules[$moduleType][$sig], 'setTemplateReference' ) )
  1426.         {
  1427.             $this->_modules[$moduleType][$sig]->setTemplateReference( $this );
  1428.         }
  1429.  
  1430.         $this->_modules[$moduleType][$sig]->setParams( $params );
  1431.  
  1432.         return $this->_modules[$moduleType][$sig];
  1433.     }
  1434.  
  1435.     /**
  1436.     * checks whether a module exists.
  1437.     *
  1438.     * Modules are located in the patTemplate folder and include:
  1439.     * - Readers
  1440.     * - Caches
  1441.     * - Variable Modifiers
  1442.     * - Filters
  1443.     * - Functions
  1444.     * - Stats
  1445.     *
  1446.     * @access    public
  1447.     * @param    string    moduleType (Reader|TemplateCache|Modifier|OutputFilter|InputFilter)
  1448.     * @param    string    moduleName
  1449.     * @return    boolean
  1450.     */
  1451.     function moduleExists( $moduleType, $moduleName )
  1452.     {
  1453.         // !!!JOOMLA VARIATION!!!
  1454.         // cache checks on files
  1455.         static $paths;
  1456.  
  1457.         if (!$paths)
  1458.         {
  1459.             $paths = array();
  1460.         }
  1461.  
  1462.         if (isset($this->_moduleDirs[$moduleType])) {
  1463.             $dirs = $this->_moduleDirs[$moduleType];
  1464.         } else {
  1465.             $dirs = array();
  1466.         }
  1467.         array_push($dirs, $this->getIncludePath() .'/'. $moduleType);
  1468.  
  1469.         foreach ($dirs as $dir) {
  1470.             $moduleFile    = sprintf( "%s/%s.php", $dir, str_replace( '_', '/', $moduleName ) );
  1471.             if (!isset( $paths[$moduleFile] ))
  1472.             {
  1473.                 if (!file_exists($moduleFile)) {
  1474.                     $paths[$moduleFile] = false;
  1475.                 }
  1476.                 else if (!is_readable($moduleFile)) {
  1477.                     $paths[$moduleFile] = false;
  1478.                 }
  1479.                 else
  1480.                 {
  1481.                     $paths[$moduleFile] = true;
  1482.                 }
  1483.             }
  1484.  
  1485.             if (!$paths[$moduleFile]) {
  1486.                 continue;
  1487.             }
  1488.             return true;
  1489.         }
  1490.         return false;
  1491.     }
  1492.  
  1493.     /**
  1494.     * parses a template
  1495.     *
  1496.     * Parses a template and stores the parsed content.
  1497.     * mode can be "w" for write (delete already parsed content) or "a" for append (appends the
  1498.     * new parsed content to the already parsed content)
  1499.     *
  1500.     * @access    public
  1501.     * @param    string    name of the template
  1502.     * @param    string    mode for the parsing
  1503.     */
  1504.     function parseTemplate( $template, $mode = 'w' )
  1505.     {
  1506.         $template = strtolower($template);
  1507.  
  1508.         if (!isset($this->_templates[$template])) {
  1509.             return    patErrorManager::raiseWarning(
  1510.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  1511.                                                     "Template '$template' does not exist."
  1512.                                                 );
  1513.         }
  1514.  
  1515.         /**
  1516.          * template is not visible
  1517.          */
  1518.         if ($this->_templates[$template]['attributes']['visibility'] == 'hidden') {
  1519.             $this->_templates[$template]['result']    =    '';
  1520.             $this->_templates[$template]['parsed']    =    true;
  1521.             return true;
  1522.         }
  1523.  
  1524.         /**
  1525.          * check, if the template has been loaded
  1526.          * and load it if necessary.
  1527.          */
  1528.         if ($this->_templates[$template]['loaded'] !== true) {
  1529.             if ($this->_templates[$template]['attributes']['parse'] == 'on') {
  1530.                 $result = $this->readTemplatesFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
  1531.             } else {
  1532.                 $result = $this->loadTemplateFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
  1533.             }
  1534.             if (patErrorManager::isError($result)) {
  1535.                 return $result;
  1536.             }
  1537.         }
  1538.  
  1539.         /**
  1540.          * check for autoclear
  1541.          */
  1542.         if(
  1543.             isset( $this->_templates[$template]['attributes']['autoclear'] ) &&
  1544.             $this->_templates[$template]['attributes']['autoclear'] == 'yes' &&
  1545.             $mode === 'w' &&
  1546.             $this->_templates[$template]['lastMode'] != 'a'
  1547.           ) {
  1548.             $this->_templates[$template]['parsed']    = false;
  1549.         }
  1550.  
  1551.         /**
  1552.          * template has been parsed and mode is not 'append'
  1553.          */
  1554.         if ($this->_templates[$template]['parsed'] === true && $mode === 'w') {
  1555.             return true;
  1556.         }
  1557.  
  1558.         $this->_templates[$template]['lastMode'] = $mode;
  1559.  
  1560.         $this->_initTemplate( $template );
  1561.  
  1562.         if (!isset($this->_vars[$template]['rows'])) {
  1563.             $this->_vars[$template]['rows']    =    array();
  1564.         }
  1565.         $loop = count( $this->_vars[$template]['rows'] );
  1566.  
  1567.         /**
  1568.          * loop at least one times
  1569.          */
  1570.         if ($loop < 1) {
  1571.             $loop = 1;
  1572.         }
  1573.  
  1574.         if (isset($this->_templates[$template]['attributes']['maxloop'])) {
  1575.             $loop = ceil( $loop / $this->_templates[$template]['attributes']['maxloop'] ) * $this->_templates[$template]['attributes']['maxloop'];
  1576.         }
  1577.  
  1578.         $this->_templates[$template]['loop'] = max( $this->_templates[$template]['attributes']['loop'], $loop );
  1579.  
  1580.         $start = 0;
  1581.         if (isset($this->_templates[$template]['attributes']['limit'])) {
  1582.             $p = strpos( $this->_templates[$template]['attributes']['limit'], ',' );
  1583.             if ($p === false) {
  1584.                 $this->_templates[$template]['loop'] = min( $this->_templates[$template]['loop'], $this->_templates[$template]['attributes']['limit'] );
  1585.                 $start = 0;
  1586.             } else {
  1587.                 $start = substr( $this->_templates[$template]['attributes']['limit'], 0, $p );
  1588.                 $end   = substr( $this->_templates[$template]['attributes']['limit'], $p+1 )+$start;
  1589.  
  1590.                 $this->_templates[$template]['loop'] = min( $this->_templates[$template]['loop'], $end );
  1591.             }
  1592.         }
  1593.  
  1594.         /**
  1595.          * template should be cleared before parsing
  1596.          */
  1597.         if ($mode == 'w') {
  1598.             $this->_templates[$template]['result']    = '';
  1599.             $this->_templates[$template]['iteration'] = $start;
  1600.         }
  1601.  
  1602.         $loopCount = 0;
  1603.         for ($i = $start; $i < $this->_templates[$template]['loop']; $i++) {
  1604.             $finished  = false;
  1605.  
  1606.             unset( $this->_templates[$template]['vars'] );
  1607.  
  1608.             /**
  1609.              * fetch the variables
  1610.              */
  1611.             $this->_fetchVariables( $template );
  1612.  
  1613.             /**
  1614.              * fetch the template
  1615.              */
  1616.             $result = $this->_fetchTemplate($template);
  1617.  
  1618.             if ($result === false) {
  1619.                 $this->_templates[$template]['iteration']++;
  1620.                 continue;
  1621.             }
  1622.  
  1623.             /**
  1624.              * parse
  1625.              */
  1626.             $this->_parseVariables( $template );
  1627.             $result = $this->_parseDependencies( $template );
  1628.             if (patErrorManager::isError($result)) {
  1629.                 return $result;
  1630.             }
  1631.  
  1632.             /**
  1633.              * store result
  1634.              */
  1635.             $this->_templates[$template]['result'] .= $this->_templates[$template]['work'];
  1636.  
  1637.             $this->_templates[$template]['iteration']++;
  1638.  
  1639.             ++$loopCount;
  1640.  
  1641.             /**
  1642.              * check for maximum loops
  1643.              */
  1644.             if (isset($this->_templates[$template]['attributes']['maxloop'])) {
  1645.                 if ($loopCount == $this->_templates[$template]['attributes']['maxloop'] && $i < ($loop-1)) {
  1646.                     $loopCount = 0;
  1647.                     $finished  = true;
  1648.                     $this->_templates[$template]['parsed'] = true;
  1649.                     $this->parseTemplate( $this->_templates[$template]['attributes']['parent'], 'a' );
  1650.                     $this->_templates[$template]['parsed'] = false;
  1651.                     $this->_templates[$template]['result'] = '';
  1652.                 }
  1653.             }
  1654.         }
  1655.  
  1656.         if (!$finished && isset($this->_templates[$template]['attributes']['maxloop'])) {
  1657.             $this->_templates[$template]['parsed'] = true;
  1658.             $this->parseTemplate( $this->_templates[$template]['attributes']['parent'], 'a', false );
  1659.             $this->_templates[$template]['parsed'] = false;
  1660.             $this->_templates[$template]['result'] = '';
  1661.             $this->_templates[$this->_templates[$template]['attributes']['parent']]['work'] = '';
  1662.         }
  1663.  
  1664.         $this->_parseGlobals($template);
  1665.  
  1666.         $this->_handleUnusedVars($template);
  1667.  
  1668.         $this->_templates[$template]['parsed']    = true;
  1669.  
  1670.         if (isset($this->_templates[$template]['attributes']['autoclear']) && $this->_templates[$template]['attributes']['autoclear'] == 'yes') {
  1671.             $this->_vars[$template] = array(
  1672.                                             'scalar' => array(),
  1673.                                             'rows'   => array()
  1674.                                             );
  1675.         }
  1676.  
  1677.         if (isset($this->_templates[$template]['attributes']['outputfilter'])) {
  1678.             if (is_object($this->_templates[$template]['attributes']['outputfilter'])) {
  1679.                 $filter = &$this->_templates[$template]['attributes']['outputfilter'];
  1680.             } else {
  1681.                 $filter = &$this->loadModule('OutputFilter', $this->_templates[$template]['attributes']['outputfilter']);
  1682.             }
  1683.  
  1684.             if (patErrorManager::isError($filter)) {
  1685.                 return $filter;
  1686.             }
  1687.  
  1688.             $this->_templates[$template]['result'] = $filter->apply($this->_templates[$template]['result']);
  1689.         }
  1690.         return true;
  1691.     }
  1692.  
  1693.     /**
  1694.     * Initialize a template
  1695.     *
  1696.     * This method checks the variable specifications and
  1697.     * copys variables from other templates.
  1698.     *
  1699.     * @access    private
  1700.     * @param    string    name of the template
  1701.     * @return    boolean    true on success
  1702.     */
  1703.     function _initTemplate( $template )
  1704.     {
  1705.         foreach( $this->_templates[$template]['copyVars'] as $dest => $src )
  1706.         {
  1707.             /**
  1708.              * copy from the same template
  1709.              */
  1710.             if( !is_array( $src ) )
  1711.             {
  1712.                 $srcTemplate = $template;
  1713.                 $srcVar      = $src;
  1714.             }
  1715.             else
  1716.             {
  1717.                 $srcTemplate = $src[0];
  1718.                 $srcVar      = $src[1];
  1719.             }
  1720.  
  1721.             $copied = false;
  1722.  
  1723.             /**
  1724.              * copy from another template
  1725.              */
  1726.             if( isset( $this->_vars[$srcTemplate] ) )
  1727.             {
  1728.                 if( isset( $this->_vars[$srcTemplate]['scalar'][$srcVar] ) )
  1729.                 {
  1730.                     $this->_vars[$template]['scalar'][$dest] = $this->_vars[$srcTemplate]['scalar'][$srcVar];
  1731.                     continue;
  1732.                 }
  1733.  
  1734.                 $rows = count( $this->_vars[$srcTemplate]['rows'] );
  1735.  
  1736.                 for( $i = 0; $i < $rows; $i++ )
  1737.                 {
  1738.                     if( !isset( $this->_vars[$srcTemplate]['rows'][$i][$srcVar] ) )
  1739.                         continue;
  1740.                     if( !isset( $this->_vars[$template]['rows'][$i] ) )
  1741.                         $this->_vars[$template]['rows'][$i] = array();
  1742.                     $this->_vars[$template]['rows'][$i][$dest] = $this->_vars[$srcTemplate]['rows'][$i][$srcVar];
  1743.                     $copied = true;
  1744.                 }
  1745.             }
  1746.             if( !$copied && isset( $this->_globals[$srcVar] ))
  1747.             {
  1748.                 $this->_vars[$template]['scalar'][$dest] = $this->_globals[$srcVar];
  1749.             }
  1750.  
  1751.         }
  1752.         return true;
  1753.     }
  1754.  
  1755.     /**
  1756.     * parse all variables in a template
  1757.     *
  1758.     * @access    private
  1759.     * @param    string
  1760.     */
  1761.     function _parseVariables( $template )
  1762.     {
  1763.         /**
  1764.          * modify variables before parsing
  1765.          */
  1766.         $this->_applyModifers($template, $this->_templates[$template]['vars']);
  1767.  
  1768.         foreach( $this->_templates[$template]['vars'] as $key => $value )
  1769.         {
  1770.             if( is_array( $value ) )
  1771.             {
  1772.                 if( count( $this->_templates[$template]['currentDependencies'] ) == 1 )
  1773.                 {
  1774.                     $child    =    $this->_templates[$template]['currentDependencies'][0];
  1775.                 }
  1776.                 else
  1777.                 {
  1778.                     if( isset( $this->_templates[$template]['attributes']['child'] ) )
  1779.                         $child = $this->_templates[$template]['attributes']['child'];
  1780.                     else
  1781.                         continue;
  1782.                 }
  1783.  
  1784.                 $this->setAttribute( $child, 'autoclear', 'yes' );
  1785.                 $this->addVar( $child, $key, $value );
  1786.                 continue;
  1787.             }
  1788.  
  1789.             $var  = $this->_startTag.$key.$this->_endTag;
  1790.             $this->_templates[$template]['work'] = str_replace( $var, $value, $this->_templates[$template]['work'] );
  1791.         }
  1792.         return true;
  1793.     }
  1794.  
  1795.     /**
  1796.     * parse global variables in the template
  1797.     *
  1798.     * @access   private
  1799.     * @param    string      name of the template
  1800.     * @return   boolean
  1801.     */
  1802.     function _parseGlobals($template)
  1803.     {
  1804.         $globalVars = $this->_globals;
  1805.         $this->_applyModifers($template, $globalVars);
  1806.  
  1807.         foreach( $globalVars as $key => $value )
  1808.         {
  1809.             if( is_array( $value ) )
  1810.             {
  1811.                 continue;
  1812.             }
  1813.  
  1814.             $var  = $this->_startTag.$key.$this->_endTag;
  1815.             $this->_templates[$template]['result'] = str_replace( $var, $value, $this->_templates[$template]['result'] );
  1816.         }
  1817.         return true;
  1818.     }
  1819.  
  1820.     /**
  1821.     * apply variable modifiers
  1822.     *
  1823.     * The variables will be passed by reference.
  1824.     *
  1825.     * @access   private
  1826.     * @param    string      name of the template (use modifiers from this template)
  1827.     * @param    array       variables to which the modifiers should be applied
  1828.     * @return   boolean
  1829.     */
  1830.     function _applyModifers($template, &$vars)
  1831.     {
  1832.         foreach ($this->_templates[$template]['modifyVars'] as $varname => $modifier) {
  1833.             if (!isset($vars[$varname])) {
  1834.                 continue;
  1835.             }
  1836.  
  1837.             if (($modifier['type'] === 'php' || $modifier['type'] === 'auto' ) && is_callable($modifier['mod'])) {
  1838.                 $vars[$varname] = call_user_func($modifier['mod'], $vars[$varname]);
  1839.                 continue;
  1840.             }
  1841.  
  1842.             if ($modifier['type'] === 'php') {
  1843.                 continue;
  1844.             }
  1845.  
  1846.             $mod = &$this->loadModule( 'Modifier', ucfirst( $modifier['mod'] ) );
  1847.             $vars[$varname] = $mod->modify( $vars[$varname], $modifier['params'] );
  1848.         }
  1849.  
  1850.         // apply the default modifier
  1851.         if (isset($this->_templates[$template]['attributes']['defaultmodifier'])) {
  1852.  
  1853.             $defaultModifier = $this->_templates[$template]['attributes']['defaultmodifier'];
  1854.             if (is_callable($defaultModifier)) {
  1855.                 $type = 'php';
  1856.             } else {
  1857.                 $type = 'custom';
  1858.                 $defaultModifier = &$this->loadModule('Modifier', ucfirst($defaultModifier));
  1859.             }
  1860.  
  1861.  
  1862.             foreach (array_keys($vars) as $varname) {
  1863.                 if (isset($this->_templates[$template]['modifyVars'][$varname])) {
  1864.                     continue;
  1865.                 }
  1866.                 if ($type === 'php') {
  1867.                     $vars[$varname] = call_user_func($defaultModifier, $vars[$varname]);
  1868.                 } else {
  1869.                     $vars[$varname] = $defaultModifier->modify($vars[$varname], array());
  1870.                 }
  1871.             }
  1872.         }
  1873.  
  1874.         return true;
  1875.     }
  1876.  
  1877.     /**
  1878.     * parse all dependencies in a template
  1879.     *
  1880.     * @access    private
  1881.     * @param    string
  1882.     */
  1883.     function _parseDependencies($template)
  1884.     {
  1885.         $countDep    =    count( $this->_templates[$template]['currentDependencies'] );
  1886.         for ($i = 0; $i < $countDep; $i++) {
  1887.             $depTemplate = $this->_templates[$template]['currentDependencies'][$i];
  1888.             if ($depTemplate == $template) {
  1889.                 return patErrorManager::raiseError(PATTEMPLATE_ERROR_RECURSION, 'You have an error in your template "' . $template . '", which leads to recursion');
  1890.             }
  1891.             $this->parseTemplate($depTemplate);
  1892.             $var    = $this->_startTag.'TMPL:'.strtoupper( $depTemplate) .$this->_endTag;
  1893.             $this->_templates[$template]['work'] = str_replace( $var, $this->_templates[$depTemplate]['result'], $this->_templates[$template]['work'] );
  1894.         }
  1895.         return true;
  1896.     }
  1897.  
  1898.     /**
  1899.     * fetch plain template
  1900.     *
  1901.     * The template content will be stored in the template
  1902.     * configuration so it can be used by other
  1903.     * methods.
  1904.     *
  1905.     * @access    private
  1906.     * @param    string    template name
  1907.     * @return    boolean
  1908.     */
  1909.     function _fetchTemplate( $template )
  1910.     {
  1911.         switch( $this->_templates[$template]['attributes']['type'] )
  1912.         {
  1913.             /**
  1914.              * condition template
  1915.              */
  1916.             case 'condition':
  1917.                 $value = $this->_getConditionValue($template, $this->_templates[$template]['attributes']['conditionvar']);
  1918.                 if ($value === false) {
  1919.                     $this->_templates[$template]['work']                = '';
  1920.                     $this->_templates[$template]['currentDependencies']    = array();
  1921.                 } else {
  1922.                     $this->_templates[$template]['work']                = $this->_templates[$template]['subtemplates'][$value]['data'];
  1923.                     $this->_templates[$template]['currentDependencies']    = $this->_templates[$template]['subtemplates'][$value]['dependencies'];
  1924.                 }
  1925.                 break;
  1926.  
  1927.             /**
  1928.              * condition template
  1929.              */
  1930.             case 'simplecondition':
  1931.                 foreach( $this->_templates[$template]['attributes']['requiredvars'] as $var )
  1932.                 {
  1933.                     // different template scope
  1934.                     if( $var[0] !== $template ) {
  1935.                         $this->_fetchVariables($var[0]);
  1936.                     }
  1937.                     $value = null;
  1938.                     // fetch the local variable
  1939.                     if( isset( $this->_templates[$var[0]]['vars'][$var[1]] )
  1940.                       && strlen( $this->_templates[$var[0]]['vars'][$var[1]] ) > 0 ) {
  1941.                        $value = $this->_templates[$var[0]]['vars'][$var[1]];
  1942.                     }
  1943.                     if (isset($this->_templates[$template]['attributes']['useglobals'])) {
  1944.                         if(isset($this->_globals[$var[1]]) && strlen($this->_globals[$var[1]]) > 1) {
  1945.                             $value = $this->_globals[$var[1]];
  1946.                         }
  1947.                     }
  1948.                     if ($value !== null) {
  1949.                         if ($var[2] === null) {
  1950.                             continue;
  1951.                         } else {
  1952.                             // Joomla! addition 23-June-2005
  1953.                             // value wrapped in ## uses regex for comparison
  1954.                             $condition = $var[2];
  1955.                             if (substr( $condition, 0, 1 ) == '#' && substr( $condition, -1, 1 ) == '#' ) {
  1956.                                 if (preg_match( $condition, $value )) {
  1957.                                     continue;
  1958.                                 }
  1959.                             } else if ($condition == $value) {
  1960.                                 continue;
  1961.                             }
  1962.                             /* Pat Original
  1963.                             if ($var[2] == $value) {
  1964.                                    continue;
  1965.                             }
  1966.                             */
  1967.                         }
  1968.                     }
  1969.  
  1970.                     $this->_templates[$template]['work']                = '';
  1971.                     $this->_templates[$template]['currentDependencies']    = array();
  1972.                     break 2;
  1973.                 }
  1974.                 $this->_templates[$template]['work']                 = $this->_templates[$template]['content'];
  1975.                 $this->_templates[$template]['currentDependencies']    = $this->_templates[$template]['dependencies'];
  1976.                 break;
  1977.  
  1978.             /**
  1979.              * modulo template
  1980.              */
  1981.             case 'modulo':
  1982.                 // check for empty template
  1983.  
  1984.                 if ($this->_hasVariables($template)) {
  1985.                     $value = (string)($this->_templates[$template]['iteration'] + 1 ) % $this->_templates[$template]['attributes']['modulo'];
  1986.                 } else {
  1987.                     $value = '__empty';
  1988.                 }
  1989.  
  1990.                 $value = $this->_getConditionValue($template, $value, false);
  1991.                 if ($value === false) {
  1992.                     $this->_templates[$template]['work']                = '';
  1993.                     $this->_templates[$template]['currentDependencies']    = array();
  1994.                 } else {
  1995.                     $this->_templates[$template]['work']                = $this->_templates[$template]['subtemplates'][$value]['data'];
  1996.                     $this->_templates[$template]['currentDependencies']    = $this->_templates[$template]['subtemplates'][$value]['dependencies'];
  1997.                 }
  1998.                 break;
  1999.  
  2000.             /**
  2001.              * standard template
  2002.              */
  2003.             default:
  2004.                 $this->_templates[$template]['work']                 =    $this->_templates[$template]['content'];
  2005.                 $this->_templates[$template]['currentDependencies']    =    $this->_templates[$template]['dependencies'];
  2006.                 break;
  2007.         }
  2008.         return true;
  2009.     }
  2010.  
  2011.     /**
  2012.     * check, whether a template contains variables
  2013.     *
  2014.     * @access   private
  2015.     * @param    string  template name
  2016.     * @return   boolean
  2017.     */
  2018.     function _hasVariables($template)
  2019.     {
  2020.         if (!empty($this->_vars[$template]['scalar'])) {
  2021.             return true;
  2022.         }
  2023.         if (isset($this->_vars[$template]['rows'][$this->_templates[$template]['iteration']])) {
  2024.             return true;
  2025.         }
  2026.         return false;
  2027.     }
  2028.  
  2029.     /**
  2030.     * fetch the value of a condition variable
  2031.     *
  2032.     * _fetchVariables() has to be called before this
  2033.     * method is being called.
  2034.     *
  2035.     * @access    private
  2036.     * @param    string    template name
  2037.     * @param    string    condition value
  2038.     * @param    boolean    flag that indicates whether value is the name of the variable that should be resolved
  2039.     *
  2040.     * @todo        split this method into smaller check methods that will be called according to
  2041.     *            a priority list
  2042.     */
  2043.     function _getConditionValue( $template, $value, $isVar = true )
  2044.     {
  2045.         if ($isVar === true) {
  2046.             if (isset($this->_templates[$template]['attributes']['conditiontmpl'])) {
  2047.                 $_template = $this->_templates[$template]['attributes']['conditiontmpl'];
  2048.                 $this->_fetchVariables($_template);
  2049.             } else {
  2050.                 $_template = $template;
  2051.             }
  2052.  
  2053.             /**
  2054.              * get the value from the template variables
  2055.              */
  2056.             if (!isset($this->_templates[$_template]['vars'][$value]) || strlen($this->_templates[$_template]['vars'][$value]) === 0) {
  2057.                 if ($this->_templates[$template]['attributes']['useglobals'] == 'yes' || $this->_templates[$template]['attributes']['useglobals'] == 'useglobals') {
  2058.                     if (isset( $this->_globals[$value] ) && strlen( $this->_globals[$value] ) > 0) {
  2059.                         $value = $this->_globals[$value];
  2060.                     } else {
  2061.                         $value = '__empty';
  2062.                     }
  2063.                 } else {
  2064.                     $value = '__empty';
  2065.                 }
  2066.             } else {
  2067.                 $value = $this->_templates[$_template]['vars'][$value];
  2068.             }
  2069.         } else {
  2070.             $_template = $template;
  2071.         }
  2072.  
  2073.         // if value is empty and a template for empty has been defined, this
  2074.         // has priority
  2075.         if ($value === '__empty' && isset($this->_templates[$template]['subtemplates']['__empty'])) {
  2076.             return $value;
  2077.         }
  2078.  
  2079.         // only one iteration (but not empty), use the __single condition
  2080.         if ($value !== '__empty' && $this->_templates[$_template]['loop'] === 1) {
  2081.             if( isset($this->_templates[$template]['subtemplates']['__single'])) {
  2082.                 return '__single';
  2083.             }
  2084.         } else {
  2085.  
  2086.             // is __first?
  2087.             if( $this->_templates[$_template]['iteration'] == 0 ) {
  2088.                 if( isset( $this->_templates[$template]['subtemplates']['__first'] ) ) {
  2089.                     return '__first';
  2090.                 }
  2091.             }
  2092.  
  2093.             /**
  2094.              * is __last?
  2095.              */
  2096.             if (isset($this->_templates[$_template]['loop'])) {
  2097.                 $max = $this->_templates[$_template]['loop'] - 1;
  2098.                 if( $this->_templates[$_template]['iteration'] == $max ) {
  2099.                     if( isset( $this->_templates[$template]['subtemplates']['__last'] ) ) {
  2100.                         return '__last';
  2101.                     }
  2102.                 }
  2103.             }
  2104.         }
  2105.  
  2106.         // search for exact match
  2107.         foreach (array_keys($this->_templates[$template]['subtemplates']) as $key) {
  2108.             if (isset($this->_templates[$template]['subtemplates'][$key]['attributes']['var'])) {
  2109.                 $var = $this->_templates[$template]['subtemplates'][$key]['attributes']['var'];
  2110.                 if (isset($this->_templates[$template]['vars'][$var])) {
  2111.                     $current = $this->_templates[$template]['vars'][$var];
  2112.                 } else {
  2113.                     $current = null;
  2114.                 }
  2115.             } else {
  2116.                 $current = $key;
  2117.             }
  2118.  
  2119.             if ((string)$value === (string)$current) {
  2120.                 return $key;
  2121.             }
  2122.         }
  2123.  
  2124.         /**
  2125.          * is __default?
  2126.          */
  2127.         if( isset( $this->_templates[$template]['subtemplates']['__default'] ) ) {
  2128.             return '__default';
  2129.         }
  2130.  
  2131.         return false;
  2132.     }
  2133.  
  2134.     /**
  2135.     * fetch variables for a template
  2136.     *
  2137.     * The variables will be stored in the template
  2138.     * configuration so they can be used by other
  2139.     * methods.
  2140.     *
  2141.     * @access    private
  2142.     * @param    string    template name
  2143.     * @return    boolean
  2144.     */
  2145.     function _fetchVariables( $template )
  2146.     {
  2147.         /**
  2148.          * variables already have been fetched
  2149.          */
  2150.         if (isset($this->_templates[$template]['vars'])) {
  2151.             return true;
  2152.         }
  2153.  
  2154.         $iteration = $this->_templates[$template]['iteration'];
  2155.  
  2156.         $vars = array();
  2157.         if( isset( $this->_templates[$template]['attributes']['varscope'] ) )
  2158.         {
  2159.             if (!is_array($this->_templates[$template]['attributes']['varscope'])) {
  2160.                 $this->_templates[$template]['attributes']['varscope'] = array($this->_templates[$template]['attributes']['varscope']);
  2161.             }
  2162.             foreach ($this->_templates[$template]['attributes']['varscope'] as $scopeTemplate) {
  2163.                 if ($this->exists($scopeTemplate)) {
  2164.                     $this->_fetchVariables( $scopeTemplate );
  2165.                     $vars = array_merge($this->_templates[$scopeTemplate]['vars'], $vars);
  2166.                 } else {
  2167.                     patErrorManager::raiseWarning(PATTEMPLATE_WARNING_NO_TEMPLATE, 'Template \''.$scopeTemplate.'\' does not exist, referenced in varscope attribute of template \''.$template.'\'');
  2168.                 }
  2169.             }
  2170.         } else {
  2171.             $vars    =    array();
  2172.         }
  2173.  
  2174.         /**
  2175.          * get the scalar variables
  2176.          */
  2177.         if( isset( $this->_vars[$template] ) && isset( $this->_vars[$template]['scalar'] ) )
  2178.         {
  2179.             $vars = array_merge( $vars, $this->_vars[$template]['scalar'] );
  2180.         }
  2181.  
  2182.         /**
  2183.          * get the row variables
  2184.          */
  2185.         if( isset( $this->_vars[$template]['rows'][$iteration] ) )
  2186.         {
  2187.             $vars = array_merge( $vars, $this->_vars[$template]['rows'][$iteration] );
  2188.         }
  2189.  
  2190.         /**
  2191.          * add some system variables
  2192.          */
  2193.         $currentRow                =    $iteration + $this->_templates[$template]['attributes']['rowoffset'];
  2194.         $vars['PAT_ROW_VAR']    =    $currentRow;
  2195.  
  2196.         if( $this->_templates[$template]['attributes']['type'] == 'modulo' )
  2197.         {
  2198.             $vars['PAT_MODULO_REP']    =    ceil( $currentRow / $this->_templates[$template]['attributes']['modulo'] );
  2199.             $vars['PAT_MODULO']        =    ( $this->_templates[$template]['iteration'] + 1 ) % $this->_templates[$template]['attributes']['modulo'];
  2200.         }
  2201.  
  2202.         if( $this->_templates[$template]['attributes']['addsystemvars'] !== false )
  2203.         {
  2204.             $vars['PATTEMPLATE_VERSION'] = $this->_systemVars['appVersion'];
  2205.             $vars['PAT_LOOPS']        =    $this->_templates[$template]['loop'];
  2206.  
  2207.             switch ($this->_templates[$template]['attributes']['addsystemvars'])
  2208.             {
  2209.                 case 'boolean':
  2210.                     $trueValue  = 'true';
  2211.                     $falseValue = 'false';
  2212.                     break;
  2213.                 case 'integer':
  2214.                     $trueValue  = '1';
  2215.                     $falseValue = '0';
  2216.                     break;
  2217.                 default:
  2218.                     $trueValue  = $this->_templates[$template]['attributes']['addsystemvars'];
  2219.                     $falseValue = '';
  2220.                     break;
  2221.             }
  2222.  
  2223.             $vars['PAT_IS_ODD']        = ( $currentRow % 2 == 1 ) ? $trueValue : $falseValue;
  2224.             $vars['PAT_IS_EVEN']    = ( $currentRow % 2 == 0 ) ? $trueValue : $falseValue;
  2225.             $vars['PAT_IS_FIRST']    = ( $currentRow == 1 ) ? $trueValue : $falseValue;
  2226.             $vars['PAT_IS_LAST']    = ( $currentRow == $this->_templates[$template]['loop'] ) ? $trueValue : $falseValue;
  2227.             $vars['PAT_ROW_TYPE']    = ( $currentRow % 2 == 1 ) ? 'odd' : 'even';
  2228.         }
  2229.  
  2230.         $this->_templates[$template]['vars'] = $vars;
  2231.         return true;
  2232.     }
  2233.  
  2234.     /**
  2235.     * handle all unused variables in a template
  2236.     *
  2237.     * This is influenced by the 'unusedvars' attribute of the
  2238.     * template
  2239.     *
  2240.     * @access    private
  2241.     * @param    string
  2242.     */
  2243.     function _handleUnusedVars( $template )
  2244.     {
  2245.         $regexp = '/([^\\\])('.$this->_startTag.'[^a-z]+[^\\\]'.$this->_endTag.')/U';
  2246.  
  2247.         switch( $this->_templates[$template]['attributes']['unusedvars'] )
  2248.         {
  2249.             case 'comment':
  2250.                 $this->_templates[$template]['result'] = preg_replace( $regexp, '<!-- \\1\\2 -->', $this->_templates[$template]['result'] );
  2251.                 break;
  2252.             case 'strip':
  2253.                 $this->_templates[$template]['result'] = preg_replace( $regexp, '\\1', $this->_templates[$template]['result'] );
  2254.                 break;
  2255.             case 'nbsp':
  2256.                 $this->_templates[$template]['result'] = preg_replace( $regexp, '\\1 ', $this->_templates[$template]['result'] );
  2257.                 break;
  2258.             case 'ignore':
  2259.                 break;
  2260.             default:
  2261.                 $this->_templates[$template]['result'] = preg_replace( $regexp, '\\1'.$this->_templates[$template]['attributes']['unusedvars'], $this->_templates[$template]['result'] );
  2262.                 break;
  2263.         }
  2264.  
  2265.         // replace quoted variables
  2266.         $regexp = '/[\\\]'.$this->_startTag.'([^a-z]+)[\\\]'.$this->_endTag.'/U';
  2267.         $this->_templates[$template]['result'] = preg_replace( $regexp, $this->_startTag.'\\1'.$this->_endTag, $this->_templates[$template]['result'] );
  2268.  
  2269.         return true;
  2270.     }
  2271.  
  2272.     /**
  2273.     * returns a parsed Template
  2274.     *
  2275.     * If the template already has been parsed, it just returns the parsed template.
  2276.     * If the template has not been loaded, it will be loaded.
  2277.     *
  2278.     * @access    public
  2279.     * @param    string     name of the template
  2280.     * @param    boolean  whether to apply output filters
  2281.     * @return    string     Content of the parsed template
  2282.     * @see        displayParsedTemplate()
  2283.     */
  2284.     function getParsedTemplate( $name = null, $applyFilters = false )
  2285.     {
  2286.         if (is_null($name)) {
  2287.             $name = $this->_root;
  2288.         }
  2289.  
  2290.         $name = strtolower( $name );
  2291.         $result = $this->parseTemplate( $name );
  2292.  
  2293.         if (patErrorManager::isError( $result )) {
  2294.             return $result;
  2295.         }
  2296.  
  2297.         if ($applyFilters === false) {
  2298.             return $this->_templates[$name]['result'];
  2299.         }
  2300.  
  2301.         $result = $this->_templates[$name]['result'];
  2302.  
  2303.         $cnt = count ($this->_outputFilters);
  2304.         for ($i = 0; $i < $cnt; $i++) {
  2305.             $result = $this->_outputFilters[$i]->apply( $result );
  2306.         }
  2307.  
  2308.         return $result;
  2309.     }
  2310.  
  2311.     /**
  2312.     * displays a parsed Template
  2313.     *
  2314.     * If the template has not been loaded, it will be loaded.
  2315.     *
  2316.     * @see        getParsedTemplate()
  2317.     * @param    string    name of the template
  2318.     * @param    boolean  whether to apply output filters
  2319.     * @return    boolean    true on success
  2320.     * @access    public
  2321.     */
  2322.     function displayParsedTemplate($name = null, $applyFilters = true)
  2323.     {
  2324.         $result = $this->getParsedTemplate($name, $applyFilters);
  2325.  
  2326.         /**
  2327.          * error happened
  2328.          */
  2329.         if (patErrorManager::isError($result)) {
  2330.             return $result;
  2331.         }
  2332.  
  2333.         echo $result;
  2334.         return true;
  2335.     }
  2336.  
  2337.     /**
  2338.     * parse a template and push the result into a variable of any other
  2339.     * template
  2340.     *
  2341.     * If the template already has been parsed, it will just be pushed into the variable.
  2342.     * If the template has not been loaded, it will be loaded.
  2343.     *
  2344.     * @access    public
  2345.     * @param    string    name of the template
  2346.     * @return    string    Content of the parsed template
  2347.     * @param    boolean    if set to true, the value will be appended to the value already stored.
  2348.     * @see        getParsedTemplate()
  2349.     * @see        addVar()
  2350.     */
  2351.     function parseIntoVar( $srcTmpl, $destTmpl, $var, $append = false )
  2352.     {
  2353.         $srcTmpl  =    strtolower( $srcTmpl );
  2354.         $destTmpl =    strtolower( $destTmpl );
  2355.         $var      = strtoupper($var);
  2356.  
  2357.         $result    =    $this->parseTemplate( $srcTmpl );
  2358.  
  2359.         if( patErrorManager::isError( $result ) )
  2360.             return $result;
  2361.  
  2362.         if( $append !== true || !isset( $this->_vars[$destTmpl]['scalar'][$var] ) )
  2363.             $this->_vars[$destTmpl]['scalar'][$var] = '';
  2364.  
  2365.         $this->_vars[$destTmpl]['scalar'][$var] .= $this->_templates[$srcTmpl]['result'];
  2366.  
  2367.         return true;
  2368.     }
  2369.  
  2370.     /**
  2371.     * clears a parsed Template
  2372.     *
  2373.     * Parsed Content, variables and the loop attribute are cleared
  2374.     *
  2375.     * If you will not be using this template anymore, then you should
  2376.     * call freeTemplate()
  2377.     *
  2378.     * @access    public
  2379.     * @param    string    name of the template
  2380.     * @param    boolean        set this to true to clear all child templates, too
  2381.     * @see        clearAllTemplates()
  2382.     * @see        freeTemplate()
  2383.     */
  2384.     function clearTemplate( $name, $recursive = false )
  2385.     {
  2386.         $name    =    strtolower( $name );
  2387.         $this->_templates[$name]['parsed']        =    false;
  2388.         $this->_templates[$name]['work']        =    '';
  2389.         $this->_templates[$name]['iteration']    =    0;
  2390.         $this->_templates[$name]['result']        =    '';
  2391.         $this->_vars[$name]                        =    array(
  2392.                                                         'scalar'    =>    array(),
  2393.                                                         'rows'        =>    array()
  2394.                                                     );
  2395.  
  2396.         if (!empty($this->_templates[$name]['defaultVars'])) {
  2397.             foreach ($this->_templates[$name]['defaultVars'] as $varname => $value) {
  2398.                 $this->addVar($name, $varname, $value);
  2399.             }
  2400.         }
  2401.  
  2402.         /**
  2403.          * clear child templates as well
  2404.          */
  2405.         if( $recursive === true )
  2406.         {
  2407.             $deps = $this->_getDependencies( $name );
  2408.             foreach( $deps as $dep )
  2409.             {
  2410.                 $this->clearTemplate( $dep, true );
  2411.             }
  2412.         }
  2413.         return true;
  2414.     }
  2415.  
  2416.     /**
  2417.     * clears all templates
  2418.     *
  2419.     * @access    public
  2420.     * @uses        clearTemplate()
  2421.     */
  2422.     function clearAllTemplates()
  2423.     {
  2424.         $templates    =    array_keys( $this->_templates );
  2425.         $cnt        =    count( $templates );
  2426.         for( $i = 0; $i < $cnt; $i++ )
  2427.         {
  2428.             $this->clearTemplate( $templates[$i] );
  2429.         }
  2430.         return true;
  2431.     }
  2432.  
  2433.     /**
  2434.     * frees a template
  2435.     *
  2436.     * All memory consumed by the template
  2437.     * will be freed.
  2438.     *
  2439.     * @access    public
  2440.     * @param    string    name of the template
  2441.     * @param    boolean    clear dependencies of the template
  2442.     * @see        freeAllTemplates()
  2443.     */
  2444.     function freeTemplate( $name, $recursive = false )
  2445.     {
  2446.         $name    =    strtolower( $name );
  2447.         $key = array_search( $name, $this->_templateList );
  2448.         if( $key === false )
  2449.         {
  2450.             return    patErrorManager::raiseWarning(
  2451.                                                     PATTEMPLATE_WARNING_NO_TEMPLATE,
  2452.                                                     "Template '$name' does not exist."
  2453.                                                 );
  2454.         }
  2455.  
  2456.         unset( $this->_templateList[$key] );
  2457.         $this->_templateList = array_values( $this->_templateList );
  2458.  
  2459.         /**
  2460.          * free child templates as well
  2461.          */
  2462.         if( $recursive === true )
  2463.         {
  2464.             $deps = $this->_getDependencies( $name );
  2465.             foreach( $deps as $dep )
  2466.             {
  2467.                 $this->freeTemplate( $dep, true );
  2468.             }
  2469.         }
  2470.  
  2471.         unset( $this->_templates[$name] );
  2472.         unset( $this->_vars[$name] );
  2473.         if (isset($this->_discoveredPlaceholders[$name])) {
  2474.             unset($this->_discoveredPlaceholders[$name]);
  2475.         }
  2476.  
  2477.         return true;
  2478.     }
  2479.  
  2480.     /**
  2481.     * frees all templates
  2482.     *
  2483.     * All memory consumed by the templates
  2484.     * will be freed.
  2485.     *
  2486.     * @access    public
  2487.     * @see        freeTemplate()
  2488.     */
  2489.     function freeAllTemplates()
  2490.     {
  2491.         $this->_templates     = array();
  2492.         $this->_vars         = array();
  2493.         $this->_templateList = array();
  2494.     }
  2495.  
  2496.     /**
  2497.     * get _all_ dependencies of a template,
  2498.     * regardless of the subtemplates
  2499.     *
  2500.     * @access    private
  2501.     * @param    string    template name
  2502.     * @return    array    list of all subtemplates
  2503.     */
  2504.     function _getDependencies( $template )
  2505.     {
  2506.         $deps = array();
  2507.         if( isset( $this->_templates[$template]['dependencies'] ) )
  2508.             $deps = $this->_templates[$template]['dependencies'];
  2509.  
  2510.         if( isset( $this->_templates[$template]['subtemplates'] ) )
  2511.         {
  2512.             foreach( $this->_templates[$template]['subtemplates'] as $sub )
  2513.             {
  2514.                 if( isset( $sub['dependencies'] ) )
  2515.                     $deps = array_merge( $deps, $sub['dependencies'] );
  2516.             }
  2517.         }
  2518.         $deps = array_unique( $deps );
  2519.         return $deps;
  2520.     }
  2521.  
  2522.     /**
  2523.     * Displays useful information about all or named templates
  2524.     *
  2525.     * This method breaks BC, as it now awaits an array instead of
  2526.     * unlimited parameters.
  2527.     *
  2528.     * @param    mixed    array of templates that should be dumped, or null if you
  2529.     *                    want all templates to be dumped
  2530.     * @param    string    dumper
  2531.     * @access    public
  2532.     */
  2533.     function dump( $restrict = null, $dumper = 'Html' )
  2534.     {
  2535.         if( is_string( $restrict ) )
  2536.             $restrict = array( $restrict );
  2537.  
  2538.         $dumper    =    &$this->loadModule( 'Dump', $dumper );
  2539.  
  2540.         if( patErrorManager::isError( $dumper ) )
  2541.         {
  2542.             return    $dumper;
  2543.         }
  2544.  
  2545.         if( is_null( $restrict ) )
  2546.         {
  2547.             $templates = $this->_templates;
  2548.             $vars      = $this->_vars;
  2549.         }
  2550.         else
  2551.         {
  2552.             $restrict = array_map( 'strtolower', $restrict );
  2553.  
  2554.             $templates = array();
  2555.             $vars      = array();
  2556.  
  2557.             foreach( $this->_templates as $name => $spec )
  2558.             {
  2559.                 if( !in_array( $name, $restrict ) )
  2560.                     continue;
  2561.                 $templates[$name] = $spec;
  2562.                 $vars[$name]      = $this->_vars[$name];
  2563.             }
  2564.         }
  2565.  
  2566.         $dumper->displayHeader();
  2567.         $dumper->dumpGlobals( $this->_globals );
  2568.         $dumper->dumpTemplates( $templates, $vars );
  2569.         $dumper->displayFooter();
  2570.  
  2571.         return    true;
  2572.     }
  2573.  
  2574.     /**
  2575.     * get the include path
  2576.     *
  2577.     * @access    public
  2578.     * @return   string
  2579.     */
  2580.     function getIncludePath()
  2581.     {
  2582.         return    PATTEMPLATE_INCLUDE_PATH;
  2583.     }
  2584.  
  2585.     /**
  2586.     * apply input filters that have been set
  2587.     *
  2588.     * This is being called by the readers.
  2589.     *
  2590.     * @access    public
  2591.     * @param    string        template
  2592.     * @return    string        filtered templeta
  2593.     */
  2594.     function applyInputFilters( $template )
  2595.     {
  2596.         $cnt = count( $this->_inputFilters );
  2597.         for( $i = 0; $i < $cnt; $i++ )
  2598.         {
  2599.             $template = $this->_inputFilters[$i]->apply( $template );
  2600.         }
  2601.         return $template;
  2602.     }
  2603.  
  2604.     /**
  2605.     * checks, whether a placeholder exists in a template
  2606.     *
  2607.     * @access   public
  2608.     * @param    string      name of the placeholder
  2609.     * @param    string      name of the template
  2610.     * @param    boolean     whether to use the cached result of a previous call
  2611.     */
  2612.     function placeholderExists($placeholder, $tmpl, $cached = true)
  2613.     {
  2614.         $tmpl = strtolower($tmpl);
  2615.         $placeholder = strtoupper($placeholder);
  2616.  
  2617.         if (!$this->exists($tmpl)) {
  2618.             return false;
  2619.         }
  2620.  
  2621.         if ($cached === true) {
  2622.             if (isset($this->_discoveredPlaceholders[$tmpl]) && isset($this->_discoveredPlaceholders[$tmpl][$placeholder])) {
  2623.                 return $this->_discoveredPlaceholders[$tmpl][$placeholder];
  2624.             }
  2625.         }
  2626.  
  2627.         if (isset($this->_templates[$tmpl]['subtemplates'])) {
  2628.             $content = '';
  2629.             foreach ($this->_templates[$tmpl]['subtemplates'] as $temp) {
  2630.                 if (!isset($temp['data'])) {
  2631.                     continue;
  2632.                 }
  2633.                 $content .= $temp['data'];
  2634.             }
  2635.         } else {
  2636.             $content = $this->_templates[$tmpl]['content'];
  2637.         }
  2638.  
  2639.         $search = $this->_startTag . $placeholder . $this->_endTag;
  2640.         if (strstr($content, $search) !== false) {
  2641.             $this->_discoveredPlaceholders[$tmpl][$placeholder] = true;
  2642.             return true;
  2643.         }
  2644.         $this->_discoveredPlaceholders[$tmpl][$placeholder] = false;
  2645.         return false;
  2646.     }
  2647.  
  2648.     /**
  2649.     * Convert the template to its string representation.
  2650.     *
  2651.     * This method allows you to just echo the patTemplate
  2652.     * object in order to display the template.
  2653.     *
  2654.     * Requires PHP5
  2655.     *
  2656.     * <code>
  2657.     * $tmpl = new patTemplate();
  2658.     * $tmpl->readTemplatesFromFile( 'myfile.tmpl' );
  2659.     * echo $tmpl;
  2660.     * </code>
  2661.     *
  2662.     * @access    private
  2663.     * @return    string
  2664.     */
  2665.     function __toString()
  2666.     {
  2667.         return $this->getParsedTemplate();
  2668.     }
  2669. }
  2670. ?>